/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frcmd_client.c                                     */
/*  NOTE      =                                                    */
/*  DATE      = 2014/12/03 by guojq                                */
/*******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include <signal.h>
#include <errno.h>

#include "frcomm.h"
#include "frmem.h"
#include "client.pb-c.h"

#define CLIENT_DEFAULT_PORT    1108

#define CLIENT_MAX_LINE        102400
#define CMD_MAX_LEN            20
#define CMD_SETUP              "setup"
#define CMD_PRINT              "print"
#define CMD_EXPORT             "export"

#define MODEL_L7               "l7"
#define MODEL_FILTER           "filter"
#define MODEL_JAVA             "java"
#define MODEL_COMMIT           "commit"
#define MODEL_ROLLBACK         "rollback"
#define MODEL_EXIT             "exit"

#define FILTER_CMD_CLEAR       "clear"

//命令结束符
#define FRCMD_SERVER_END        "broada"

FR_STATIC int g_i_port = CLIENT_DEFAULT_PORT;
FR_STATIC char* g_p_server = "127.0.0.1";

/* 字符串转unsigned int */
#define ptoui(p) ((unsigned int) \
                 ((unsigned int)*((const unsigned char *)(p + 3)) << 24 | \
                 (unsigned int)*((const unsigned char *)(p + 2)) << 16 | \
                 (unsigned int)*((const unsigned char *)(p + 1)) << 8 | \
                 (unsigned int)*((const unsigned char *)(p + 0)) << 0))

FR_STATIC struct option lopts[] = {
    {"help", 0, NULL, 0},
    {"port", 1, NULL, 'p'},
    {"cmd", 1, NULL, 'c'}
};

void frcmd_usage(void)
{
    printf("Usage: \n\t./client -h IP [-p PORT] [-c CMDLINE]\n");
    printf("\t-h\tserver host\n");
    printf("\t\texample:\n");
    printf("\t\t\t-h 127.0.0.1\n");
    printf("\t-p\tserver port\n");
    printf("\t\texample:\n");
    printf("\t\t\t-p 1200(default:1108)\n");
    printf("\t-c\tcommand\n");
    printf("\t\tformat:\n");
    printf("\t\t\t-c \"setup module cmd path l7_proto l7_proto_name mem_size;\"\n");
    printf("\t\t\t-c \"setup l7 cmd ip_proto ip port-range [l7_proto];\"\n");
    printf("\t\t\t-c \"setup l7_default cmd ip_proto l7_proto;\"\n");
    printf("\t\t\t-c \"setup filter cmd [mac] [ip_proto] [ip-range] [port-range] [ip-range] [port-range];\"\n");
    printf("\t\t\t-c \"setup java family ipaddr port;\"\n");
    printf("\t\t\t-c \"commit cmd;\"\n");
    printf("\t\t\t-c \"print ct [L7_protocol];\"\n");
    printf("\t\t\t-c \"print system;\"\n");
    printf("\t\t\t-c \"print memory;\"\n");
    printf("\t\t\t-c \"print module stat/config;\"\n");
    printf("\t\t\t-c \"exit;\"\n");
    printf("\t\texample:\n");
    printf("\t\t\t-c \"setup l7 module ../oracle/oracle_1.0.so 1521 oracle;\"\n");
    printf("\t\t\t-c \"setup l7 add tcp 192.168.0.1 80 http;\"\n");
    printf("\t\t\t-c \"setup l7 del tcp 192.168.0.1 80;\"\n");
    printf("\t\t\t-c \"setup l7 add 0.0.0.0 0;\"\n");
    printf("\t\t\t-c \"setup l7_default add tcp oracle;\"\n");
    printf("\t\t\t-c \"setup filter add tcp 192.168.0.1-192.168.0.20 80-100 0.0.0.0;\"\n");
    printf("\t\t\t-c \"setup filter del 02:ea:b3:a6:98:79 tcp 192.168.0.1-192.168.0.20 80-100 0.0.0.0;\"\n");
    printf("\t\t\t-c \"setup filter add 02:ea:b3:a6:98:79;\"\n");
    printf("\t\t\t-c \"setup filter clear;\"\n");
    printf("\t\t\t-c \"setup java tcp 1108;\"\n");
    printf("\t\t\t-c \"setup commit filter;\"\n");
    printf("\t\t\t-c \"setup rollback filter;\"\n");
    printf("\t\t\t-c \"print system;\"\n");
    printf("\t\t\t-c \"print ct [L7_protocol];\"\n");
    printf("\t\t\t-c \"print module stat;\"\n");
    printf("\t\t\t-c \"export pcap ip:port;\"\n");
    printf("\t--help\n");
    printf("\n");
}


/*******************************************************************************
*  FUNC     :  处理应答数据
*  ARGS     :  p_response     应答的数据
*  RTN      :
*  NOTE     :
*******************************************************************************/
FR_STATIC void __frcmd_response(char *p_response)
{
    printf("%s", p_response);

    return;
}

/*******************************************************************************
*  FUNC     :  接受服务端信息
*  ARGS     :  i_sockfd(socket句柄)
*  RTN      :  FR_SUCC (success)
*           :  FR_FAIL (failed)
*  NOTE     :
*******************************************************************************/
FR_STATIC int __receive_data(int i_sockfd)
{
    int i_nbytes = 0;
    int i_first_flag = 1;
    int i_copy = 0;
    int i_tmp_len = 0;      //计数拷贝了多少字节的长度字段
    unsigned int ui_data_len = 0;
    fd_set read_flags;
    struct timeval st_wait;
    char *p_tmp = NULL;
    char *p_now = NULL, *p_start = NULL;
    char arr_receive_buffer[CLIENT_MAX_LINE + 1];
    char arr_len[4] = {'\0'};
    FILE *fp = NULL;

    while (1)
    {
        /* 接收来自服务器的响应信息，判断是否握手成功 */
        st_wait.tv_sec = 3;
        st_wait.tv_usec = 0;
        FD_ZERO(&read_flags);
        FD_SET(i_sockfd, &read_flags);

        if (select(i_sockfd + 1, &read_flags, 0, 0, &st_wait) <= 0)
            goto err;

        if(FD_ISSET(i_sockfd, &read_flags))
        {
            /* 接收来自服务器的响应信息 */
            i_nbytes = recv(i_sockfd, arr_receive_buffer, sizeof(arr_receive_buffer) - 1, 0);
            if (i_nbytes <= 0)
                goto err;
            p_tmp = arr_receive_buffer;
            
            while (i_nbytes)
            {
                /* 第一次接受到的数据前四位是该次响应数据的总长度 */
                if (i_first_flag == 1)
                {
                    //取长度 (可能长度分散在2次recv中，因此将长度先拷贝出来)
                    if (i_nbytes < 4 - i_tmp_len)
                    {
                        //下一次recv才能把长度收集全
                        memcpy(arr_len + i_tmp_len, p_tmp, i_nbytes);
                        i_tmp_len += i_nbytes;
                        break;
                    }
                    i_first_flag = 0;
                    memcpy(arr_len + i_tmp_len, p_tmp, 4 - i_tmp_len);
                    
                    //算出长度
                    ui_data_len = ptoui(arr_len);
                    
                    p_start = frmalloc(ui_data_len + 1);
                    if (p_start == NULL)
                        goto err;
                        
                    memset(p_start, 0x00, ui_data_len + 1);
                    p_now = p_start;
    
                    p_tmp += 4 - i_tmp_len;
                    i_nbytes -= 4 - i_tmp_len;
                    i_tmp_len = 0;
                    fp = fopen("output1.txt","w");
                    fprintf(fp,"%s",p_tmp);
                    fclose(fp);
                }
                if (i_nbytes > ui_data_len)
                {
                    i_copy = ui_data_len;
                    i_nbytes -= ui_data_len;
                    ui_data_len = 0;
                }
                else
                {
                    i_copy = i_nbytes;
                    i_nbytes = 0;
                    ui_data_len -= i_copy;
                }
                
                memcpy(p_now, p_tmp, i_copy);
                p_now += i_copy;
                p_tmp += i_copy;
    
                if (ui_data_len == 0)
                {
                    //判断是否有结束符，有结束符说明命令结束，否则继续接收信息
                    if (strcasecmp(p_start, FRCMD_SERVER_END) == 0)
                        return FR_SUCC;
                    __frcmd_response(p_start);
                    p_start = NULL;
                    i_first_flag = 1;
                }
            }
        }
        // !FD_ISSET(i_sockfd, &read_flags)
        else
            goto err;
    }

err:
    if (p_start)
        frfree(p_start);
    return FR_FAIL;
}

/*******************************************************************************
*  FUNC     :  连接服务端
*  ARGS     :  p_addr(IP地址)
*           :  us_port(端口号)
*           :  p_sockfd(socket句柄)
*  RTN      :  FR_SUCC (success)
*           :  FR_FAIL (failed)
*  NOTE     :
*******************************************************************************/
FR_STATIC int __init_socket(const char *p_addr
                       , unsigned short us_port
                       , int *p_sockfd)
{
    int i_sockfd = 0;
    struct sockaddr_in server_addr;

    /* 客户程序开始建立 sockfd描述符 */
    if ((i_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        frdbg("socket init failed\n");
        return FR_FAIL;
    }

    /* 客户程序填充服务端的资料 */
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(us_port);
    server_addr.sin_addr.s_addr= inet_addr(p_addr);

    /* 客户程序发起连接请求 */
    if (connect(i_sockfd, (struct sockaddr *) (&server_addr), sizeof(struct sockaddr)) == -1)
        return FR_FAIL;
    
    *p_sockfd = i_sockfd;
    
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  发送消息给服务端
*  ARGS     :  i_sockfd         (socket句柄)
*           :  p_cmd            (发送的内容)
*  RTN      :  FR_SUCC          (success)
*           :  FR_FAIL          (failed)
*  NOTE     :
*******************************************************************************/
FR_STATIC int __send_data(int i_sockfd, char *p_cmd, unsigned int ui_cmd_len)
{
    int i_send = 0, i_totalsend = 0;
    int i_nbytes = 0;
    
    if (p_cmd == NULL)
        return FR_FAIL;
    
    i_nbytes = ui_cmd_len;
    while (i_totalsend < i_nbytes)
    {
        i_send = send(i_sockfd, p_cmd + i_totalsend, i_nbytes - i_totalsend, 0);
        if (i_send <= 0)
        {
            frdbg("send error!\n");
            return FR_FAIL;
        }
        i_totalsend += i_send;
    }
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  客户端和服务端之间的数据交互
*  ARGS     :  p_cmd        需要发送的数据
*           :  ui_cmdlen     数据长度
*  RTN      :  FR_SUCC (success)
*           :  FR_FAIL (failed)
*  NOTE     :
*******************************************************************************/
int frcmd_talking(char *p_cmd, unsigned int ui_cmdlen)
{
    int i_ret = 0;
    int i_sockfd = 0;

    if (g_i_port <= 0 || g_i_port > 65535 || g_p_server == NULL || p_cmd == NULL || ui_cmdlen == 0)
    {
        frcmd_usage();
        return FR_FAIL;
    }

    /* 参数中读取IP地址和PORT */
    i_ret = __init_socket(g_p_server, (unsigned short)g_i_port, &i_sockfd);
    if (i_ret != FR_SUCC)
    {
        frdbg("connect to server faild\n");
        return FR_FAIL;
    }
    
    /* 发送数据 */
    i_ret = __send_data(i_sockfd, p_cmd, ui_cmdlen);
    if (i_ret != FR_SUCC)
    {
        close(i_sockfd);
        return FR_FAIL;
    }

    i_ret = __receive_data(i_sockfd);
    if (i_ret != FR_SUCC)
    {
        printf("__receive_data failed\n");
        close(i_sockfd);
        return FR_FAIL;
    }
    close(i_sockfd);

    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  主程序
*  ARGS     :  argc(入力参数个数)
*           :  argv(入力参数)
*  RTN      :  0 (success)
*           :  1 (failed)
*  NOTE     :
*******************************************************************************/
int main_tmp(int argc, char *argv[])
{
    int i_opt_flag = 0, i_opt_index = 0, i_exit_flag = 1;
    int i_ret_fun = 0;

    i_ret_fun = fr_slab_init(20971520, "frcmd_client");//20M
    if(i_ret_fun == FR_FAIL)
    {
        printf("fr_slab_init err\n");
        return 1;
    }
    //获取程序参数
    while((i_opt_flag = getopt_long(argc,argv,"h:p:c:", lopts, &i_opt_index))!=-1)
    {
        switch(i_opt_flag)
        {
            case 'h':
                //frdbg("option h:[%s]\n",optarg);
                g_p_server = optarg;
                i_exit_flag = 0;
                break;
            case 'p':
                //frdbg("option p:[%s]\n",optarg);
                g_i_port = atoi(optarg);
                break;
            case 'c':
                yy_scan_string(optarg);
                yyparse();
                fr_slab_destroy();
                return 0;
            case 0:
                if (strcmp(lopts[i_opt_index].name, "help") == 0)
                {
                    frcmd_usage();
                    fr_slab_destroy();
                    return 1;
                }
                break;
            default:
                i_exit_flag = 1;
                frcmd_usage();
                fr_slab_destroy();
                return 1;
        }
    }

    if (i_exit_flag == 1)
  	{
  	    frcmd_usage();
  	    fr_slab_destroy();
        return 1;
    }

    yyparse();
    fr_slab_destroy();

    return 0;
}
